feat: add shared database-backed policy bundle - #14426
Conversation
WalkthroughThis change adds a shared immutable policy-bundle service, durable revision storage, migration support, superuser administration endpoints, runtime synchronization, legacy compatibility paths, and comprehensive tests. ChangesShared policy bundle
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 7 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (7 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Migration Validation Passed All migrations follow the Expand-Contract pattern correctly. |
✅ Test Coverage AdvisorNo source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## release-1.12.0 #14426 +/- ##
==================================================
+ Coverage 61.95% 63.23% +1.28%
==================================================
Files 2417 2390 -27
Lines 242524 243207 +683
Branches 36184 35173 -1011
==================================================
+ Hits 150251 153789 +3538
+ Misses 90354 87493 -2861
- Partials 1919 1925 +6
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
25f0ff6 to
89d0cf5
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/backend/tests/unit/api/v1/test_policy_bundle.py (1)
295-324: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAdd rollback conflict coverage.
Configure
rollback_policy_bundle_stateto raisePolicyBundleRevisionConflictError. Assert HTTP 409 and confirm thatapply_policy_bundle_stateis not called. This endpoint currently tests only its success response.As per coding guidelines, “API endpoint tests in the backend should verify both success and error responses.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/api/v1/test_policy_bundle.py` around lines 295 - 324, Add a conflict-path test alongside test_rollback_endpoint_creates_and_publishes_a_new_revision by configuring rollback_state (the rollback_policy_bundle_state mock) to raise PolicyBundleRevisionConflictError. Post the rollback request and assert HTTP 409, then verify apply_state (apply_policy_bundle_state) was not called.Source: Coding guidelines
🧹 Nitpick comments (10)
src/backend/tests/unit/alembic/test_shared_policy_bundle_migration.py (1)
222-244: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd negative tests for the migration's fail-closed guards.
The three tests cover the success and repair paths. The two
RuntimeErrorguards in the migration have no coverage:
upgraderaises "Shared policy bundle schema is partially initialized with durable data" when exactly one table exists and it holds rows._seed_active_bundleraises "Active policy bundle points to a missing immutable revision" when the active pointer references an absent revision.These guards prevent a corrupt bundle state from being treated as valid. Add a test for each so a regression cannot turn a loud failure into a silent one. The existing
_create_legacy_policy_tablesandmigration._create_revision_tableseams make both cases cheap to set up.As per coding guidelines: "Backend test files should ... be organized with descriptive test function names, logical setup/teardown, and coverage for positive, negative, edge, and error cases."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/alembic/test_shared_policy_bundle_migration.py` around lines 222 - 244, Add two negative migration tests covering the fail-closed guards: one should create exactly one bundle table with durable rows and assert upgrade raises the expected partial-initialization RuntimeError; the other should create the active bundle pointer referencing a nonexistent immutable revision and assert _seed_active_bundle raises its missing-revision RuntimeError. Use _create_legacy_policy_tables and migration._create_revision_table for setup, and give each test a descriptive name.Source: Coding guidelines
src/backend/base/langflow/alembic/versions/f7a9c2d4e6b8_add_shared_policy_bundle.py (1)
325-332: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low valueConsider dropping a single leftover table during downgrade.
When only one of the two tables exists,
downgradereturns without dropping it. Alembic still records the revision as reverted, so an emptypolicy_bundle_revisionorpolicy_bundle_activetable stays in the schema. The upgrade path repairs that state, so the impact is limited to a leftover empty table. Dropping whichever table exists makes the downgrade fully reversible.♻️ Proposed change
def downgrade() -> None: """Copy the active bundle to legacy stores before removing new tables.""" conn = op.get_bind() - if not migration.table_exists(REVISION_TABLE, conn) or not migration.table_exists(ACTIVE_TABLE, conn): - return - _sync_legacy_policy(conn) - op.drop_table(ACTIVE_TABLE) - op.drop_table(REVISION_TABLE) + revision_exists = migration.table_exists(REVISION_TABLE, conn) + active_exists = migration.table_exists(ACTIVE_TABLE, conn) + if revision_exists and active_exists: + _sync_legacy_policy(conn) + if active_exists: + op.drop_table(ACTIVE_TABLE) + if revision_exists: + op.drop_table(REVISION_TABLE)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/base/langflow/alembic/versions/f7a9c2d4e6b8_add_shared_policy_bundle.py` around lines 325 - 332, Update downgrade() to handle partial table state: synchronize legacy policy data only when both REVISION_TABLE and ACTIVE_TABLE exist, then drop each of those tables independently when it exists. Remove the early return that currently preserves a lone table, while retaining safe existence checks before every drop.src/lfx/src/lfx/services/manager.py (1)
536-541: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake the load-failure path as strict as the type check.
Line 539 raises
RuntimeErrorwhen the configured class has the wrong type. However, Line 512 returns silently whenload_object_from_import_pathreturnsNoneforPOLICY_BUNDLE_SERVICE. An operator who configures a policy bundle service that fails to import then starts with the built-in allow-all bundle service and no error. Align the two paths, as done forMODEL_PROVIDER_POLICY_SERVICE.♻️ Proposed change at Lines 512-519
if service_class is None: - if service_type == ServiceType.MODEL_PROVIDER_POLICY_SERVICE: + if service_type in { + ServiceType.MODEL_PROVIDER_POLICY_SERVICE, + ServiceType.POLICY_BUNDLE_SERVICE, + }: msg = ( - "Configured model provider policy service could not be loaded; " - "refusing to start with the OSS allow-all fallback" + f"Configured {service_type.value} could not be loaded; " + "refusing to start with the OSS allow-all fallback" ) raise RuntimeError(msg) return🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lfx/src/lfx/services/manager.py` around lines 536 - 541, Update the policy bundle service loading path around load_object_from_import_path so a None result raises a RuntimeError instead of silently falling back to the built-in allow-all service. Match the existing strict failure behavior used for MODEL_PROVIDER_POLICY_SERVICE, while preserving the BasePolicyBundleService subclass validation in the POLICY_BUNDLE_SERVICE branch.src/backend/tests/unit/services/database/test_migration_downgrade.py (2)
16-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe fixture uses a URL shape that
__init__never produces.
_servicebypasses__init__withobject.__new__, then setsdatabase_urlto the sync formsqlite:////configured/production.db. A realDatabaseServicerewrites this value through_sanitize_database_url, so the attribute holdssqlite+aiosqlite:////configured/production.dbat runtime. The assertion at Line 38 therefore locks in a URL shape that production never reaches.The stub on
_current_alembic_revisionscompounds this: it removes the only code in the downgrade path that callssa.create_enginewith that URL. Use the post-sanitization URL in the fixture, and add one test that exercises_current_alembic_revisionsagainst a real in-memory SQLite database.As per coding guidelines: "Warn when backend pytest files rely on excessive mocks that obscure what is actually being tested."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/services/database/test_migration_downgrade.py` around lines 16 - 22, The _service fixture does not represent the post-sanitization DatabaseService state and mocks away engine creation. Set database_url to the aiosqlite-sanitized form, remove the _current_alembic_revisions stub where appropriate, and add a test that exercises _current_alembic_revisions against a real in-memory SQLite database so the downgrade path validates actual engine usage.Source: Coding guidelines
41-52: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCover the multi-head and empty-revision guard cases.
The guard uses set equality, so it also refuses a database with multiple Alembic heads and a database with no
alembic_versionrows. The second case produces the distinctfound nonemessage. Parametrizecurrent_revisionsover{"later_revision"},{CURRENT_REVISION, "other_head"}, andset()to lock in all three refusals.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/services/database/test_migration_downgrade.py` around lines 41 - 52, The test test_explicit_downgrade_refuses_an_unexpected_database_revision should be parametrized over current_revisions values {"later_revision"}, {CURRENT_REVISION, "other_head"}, and set(). Update the expected RuntimeError match so the empty set asserts the distinct “found none” message, while the single- and multi-head cases assert their corresponding revision details; keep downgrade.assert_not_called() for every case.src/backend/base/langflow/services/catalog_policy/service.py (2)
70-79: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider caching the derived catalog snapshot per bundle revision.
snapshotbuilds a newCatalogPolicySnapshoton every access. The base class callsself.snapshotinsideis_component_blockedandis_template_blocked, so a loop over many component keys constructs one object per key. The underlying frozensets are shared by reference, so a memoized projection keyed on the current bundle snapshot identity removes the repeated construction without changing behavior.♻️ Proposed memoization of the projected snapshot
def __init__( self, database_service: DatabaseService | None, policy_bundle_service: BasePolicyBundleService | None = None, ) -> None: super().__init__() self.database_service = database_service self._policy_bundle_service = policy_bundle_service self._legacy_snapshot = CatalogPolicySnapshot() self._legacy_hydrated = False + self._projected_source: PolicyBundleSnapshot | None = None + self._projected_snapshot = CatalogPolicySnapshot() self._write_lock = asyncio.Lock() @@ def snapshot(self) -> CatalogPolicySnapshot: """Return the current immutable process-local snapshot.""" if self._policy_bundle_service is None: return self._legacy_snapshot bundle = self._policy_bundle_service.snapshot - return CatalogPolicySnapshot( - blocked_component_keys=bundle.blocked_component_keys, - blocked_template_keys=bundle.blocked_template_keys, - ) + # One atomic read of the published reference, then an identity check. + if self._projected_source is not bundle: + self._projected_snapshot = CatalogPolicySnapshot( + blocked_component_keys=bundle.blocked_component_keys, + blocked_template_keys=bundle.blocked_template_keys, + ) + self._projected_source = bundle + return self._projected_snapshot🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/base/langflow/services/catalog_policy/service.py` around lines 70 - 79, Memoize the derived CatalogPolicySnapshot in the snapshot property using the current _policy_bundle_service.snapshot identity or revision as the cache key. Reuse the cached projection while the bundle is unchanged, and rebuild and replace it when the bundle changes; preserve the _legacy_snapshot path when no policy bundle service exists.
168-196: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueConsider a bounded retry when the facet write loses the compare-and-swap.
This branch reads the active revision and then replaces it in the same transaction. A concurrent component edit and template edit therefore make one caller receive a 409, even though the two facets do not overlap. The endpoint does not retry, so an operator must resubmit. A bounded retry loop that re-reads the current revision and re-applies only the requested facet would keep the atomic full-bundle write while removing the avoidable conflict.
Keep the current behavior if you want every conflict to be operator-visible.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/base/langflow/services/catalog_policy/service.py` around lines 168 - 196, Add a bounded retry around the read-and-replace flow in the catalog policy update method, re-reading the latest bundle state after a compare-and-swap conflict and reapplying only the requested component or template facet while preserving the other facet. Keep the atomic replace_policy_bundle_state write and return the committed snapshot/diff on success; propagate the conflict after the retry limit is exhausted.src/backend/tests/unit/services/test_model_provider_policy_store.py (1)
156-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the catalog facet that this test names.
The test name states that an external provider does not block the database-owned catalog refresh. The snapshot sets
blocked_component_keys={"PythonREPL"}, but no assertion checks thatcatalog_serviceobserves it. Add the catalog assertion so the test covers the named behavior.As per coding guidelines: "verify the tests actually cover the new or changed behavior rather than acting as placeholders".💚 Proposed assertion
assert policy_store.apply_model_provider_policy_state(state) is True assert bundle_service.snapshot is snapshot + assert catalog_service.snapshot.blocked_component_keys == frozenset({"PythonREPL"}) assert provider_service.approved_provider_ids == frozenset({"openai"})🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/tests/unit/services/test_model_provider_policy_store.py` around lines 156 - 158, Update the test around apply_model_provider_policy_state to assert that catalog_service observes the snapshot’s blocked component key, including "PythonREPL". Keep the existing bundle and provider assertions unchanged so the test verifies the catalog facet named by the test behavior.Source: Coding guidelines
src/backend/base/langflow/api/v1/policy_bundle.py (1)
52-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winBound the catalog key lists like the provider list.
approved_provider_idslimits the list to 1000 entries and constrains each ID with a pattern andmax_length=255.blocked_component_keysandblocked_template_keysaccept an unbounded number of entries with unbounded length._normalize_keysonly rejects empty strings. An oversized payload is then persisted as JSON and hashed.Add a list bound and a per-key length bound for parity.
♻️ Proposed bounds
+CatalogKey = Annotated[str, StringConstraints(min_length=1, max_length=255)] + + class PolicyBundleWrite(BaseModel): """Complete replacement guarded by the caller's observed revision.""" expected_revision: int = Field(ge=1) approved_provider_ids: Annotated[list[ProviderId], Field(max_length=1000)] - blocked_component_keys: list[str] - blocked_template_keys: list[str] + blocked_component_keys: Annotated[list[CatalogKey], Field(max_length=5000)] + blocked_template_keys: Annotated[list[CatalogKey], Field(max_length=5000)]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/base/langflow/api/v1/policy_bundle.py` around lines 52 - 55, Update the policy bundle model fields blocked_component_keys and blocked_template_keys to enforce the same catalog constraints as approved_provider_ids: cap each list at 1000 entries and limit each key to 255 characters. Preserve the existing key normalization while applying these bounds through the field/type declarations.src/backend/base/langflow/api/v1/catalog_policy.py (1)
51-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShare one revision-conflict response builder. Both changed sites construct the same
409detail dictionary withmessage,expected_revision, andactive_revision. A third copy exists insrc/backend/base/langflow/api/v1/policy_bundle.pyat lines 134-142. The shared root cause is a missing common helper, so the client-facing conflict contract can drift between endpoints.
src/backend/base/langflow/api/v1/catalog_policy.py#L51-L59: remove_revision_conflictand import the shared builder.src/backend/base/langflow/api/v1/model_provider_policy.py#L147-L155: replace the inlineHTTPExceptionconstruction with the same shared builder.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/base/langflow/api/v1/catalog_policy.py` around lines 51 - 59, Introduce or reuse one shared revision-conflict response builder, preserving the existing 409 detail contract with message, expected_revision, and active_revision. In src/backend/base/langflow/api/v1/catalog_policy.py#L51-L59, remove _revision_conflict and import the shared builder; in src/backend/base/langflow/api/v1/model_provider_policy.py#L147-L155, replace the inline HTTPException construction with it. Also ensure the existing builder in src/backend/base/langflow/api/v1/policy_bundle.py#L134-L142 is the common implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/backend/base/langflow/services/database/service.py`:
- Around line 687-694: Update DatabaseService._current_alembic_revisions to use
a sync-compatible database URL for every dialect, including converting
sqlite+aiosqlite URLs produced by _sanitize_database_url, or reuse an existing
synchronous engine instead of creating a second one. Add or update coverage in
src/backend/tests/unit/services/database/test_migration_downgrade.py at lines
16-22 to verify legacy revision reads work with the sanitized SQLite URL.
---
Outside diff comments:
In `@src/backend/tests/unit/api/v1/test_policy_bundle.py`:
- Around line 295-324: Add a conflict-path test alongside
test_rollback_endpoint_creates_and_publishes_a_new_revision by configuring
rollback_state (the rollback_policy_bundle_state mock) to raise
PolicyBundleRevisionConflictError. Post the rollback request and assert HTTP
409, then verify apply_state (apply_policy_bundle_state) was not called.
---
Nitpick comments:
In
`@src/backend/base/langflow/alembic/versions/f7a9c2d4e6b8_add_shared_policy_bundle.py`:
- Around line 325-332: Update downgrade() to handle partial table state:
synchronize legacy policy data only when both REVISION_TABLE and ACTIVE_TABLE
exist, then drop each of those tables independently when it exists. Remove the
early return that currently preserves a lone table, while retaining safe
existence checks before every drop.
In `@src/backend/base/langflow/api/v1/catalog_policy.py`:
- Around line 51-59: Introduce or reuse one shared revision-conflict response
builder, preserving the existing 409 detail contract with message,
expected_revision, and active_revision. In
src/backend/base/langflow/api/v1/catalog_policy.py#L51-L59, remove
_revision_conflict and import the shared builder; in
src/backend/base/langflow/api/v1/model_provider_policy.py#L147-L155, replace the
inline HTTPException construction with it. Also ensure the existing builder in
src/backend/base/langflow/api/v1/policy_bundle.py#L134-L142 is the common
implementation.
In `@src/backend/base/langflow/api/v1/policy_bundle.py`:
- Around line 52-55: Update the policy bundle model fields
blocked_component_keys and blocked_template_keys to enforce the same catalog
constraints as approved_provider_ids: cap each list at 1000 entries and limit
each key to 255 characters. Preserve the existing key normalization while
applying these bounds through the field/type declarations.
In `@src/backend/base/langflow/services/catalog_policy/service.py`:
- Around line 70-79: Memoize the derived CatalogPolicySnapshot in the snapshot
property using the current _policy_bundle_service.snapshot identity or revision
as the cache key. Reuse the cached projection while the bundle is unchanged, and
rebuild and replace it when the bundle changes; preserve the _legacy_snapshot
path when no policy bundle service exists.
- Around line 168-196: Add a bounded retry around the read-and-replace flow in
the catalog policy update method, re-reading the latest bundle state after a
compare-and-swap conflict and reapplying only the requested component or
template facet while preserving the other facet. Keep the atomic
replace_policy_bundle_state write and return the committed snapshot/diff on
success; propagate the conflict after the retry limit is exhausted.
In `@src/backend/tests/unit/alembic/test_shared_policy_bundle_migration.py`:
- Around line 222-244: Add two negative migration tests covering the fail-closed
guards: one should create exactly one bundle table with durable rows and assert
upgrade raises the expected partial-initialization RuntimeError; the other
should create the active bundle pointer referencing a nonexistent immutable
revision and assert _seed_active_bundle raises its missing-revision
RuntimeError. Use _create_legacy_policy_tables and
migration._create_revision_table for setup, and give each test a descriptive
name.
In `@src/backend/tests/unit/services/database/test_migration_downgrade.py`:
- Around line 16-22: The _service fixture does not represent the
post-sanitization DatabaseService state and mocks away engine creation. Set
database_url to the aiosqlite-sanitized form, remove the
_current_alembic_revisions stub where appropriate, and add a test that exercises
_current_alembic_revisions against a real in-memory SQLite database so the
downgrade path validates actual engine usage.
- Around line 41-52: The test
test_explicit_downgrade_refuses_an_unexpected_database_revision should be
parametrized over current_revisions values {"later_revision"},
{CURRENT_REVISION, "other_head"}, and set(). Update the expected RuntimeError
match so the empty set asserts the distinct “found none” message, while the
single- and multi-head cases assert their corresponding revision details; keep
downgrade.assert_not_called() for every case.
In `@src/backend/tests/unit/services/test_model_provider_policy_store.py`:
- Around line 156-158: Update the test around apply_model_provider_policy_state
to assert that catalog_service observes the snapshot’s blocked component key,
including "PythonREPL". Keep the existing bundle and provider assertions
unchanged so the test verifies the catalog facet named by the test behavior.
In `@src/lfx/src/lfx/services/manager.py`:
- Around line 536-541: Update the policy bundle service loading path around
load_object_from_import_path so a None result raises a RuntimeError instead of
silently falling back to the built-in allow-all service. Match the existing
strict failure behavior used for MODEL_PROVIDER_POLICY_SERVICE, while preserving
the BasePolicyBundleService subclass validation in the POLICY_BUNDLE_SERVICE
branch.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2037d55d-094f-4b49-b141-e788ab16fd2e
📒 Files selected for processing (38)
src/backend/base/langflow/alembic/versions/f7a9c2d4e6b8_add_shared_policy_bundle.pysrc/backend/base/langflow/api/router.pysrc/backend/base/langflow/api/v1/__init__.pysrc/backend/base/langflow/api/v1/catalog_policy.pysrc/backend/base/langflow/api/v1/model_provider_policy.pysrc/backend/base/langflow/api/v1/policy_bundle.pysrc/backend/base/langflow/services/catalog_policy/factory.pysrc/backend/base/langflow/services/catalog_policy/service.pysrc/backend/base/langflow/services/database/models/__init__.pysrc/backend/base/langflow/services/database/models/policy_bundle/__init__.pysrc/backend/base/langflow/services/database/models/policy_bundle/model.pysrc/backend/base/langflow/services/database/service.pysrc/backend/base/langflow/services/deps.pysrc/backend/base/langflow/services/factory.pysrc/backend/base/langflow/services/model_provider_policy.pysrc/backend/base/langflow/services/policy_bundle.pysrc/backend/base/langflow/services/schema.pysrc/backend/base/langflow/services/task/model_provider_policy_refresh.pysrc/backend/base/langflow/services/utils.pysrc/backend/tests/unit/alembic/test_shared_policy_bundle_migration.pysrc/backend/tests/unit/api/v1/test_catalog_policy.pysrc/backend/tests/unit/api/v1/test_model_provider_policy.pysrc/backend/tests/unit/api/v1/test_policy_bundle.pysrc/backend/tests/unit/services/database/test_migration_downgrade.pysrc/backend/tests/unit/services/test_model_provider_policy_refresh.pysrc/backend/tests/unit/services/test_model_provider_policy_store.pysrc/backend/tests/unit/services/test_policy_bundle_store.pysrc/lfx/src/lfx/services/catalog_policy/__init__.pysrc/lfx/src/lfx/services/catalog_policy/base.pysrc/lfx/src/lfx/services/catalog_policy/service.pysrc/lfx/src/lfx/services/deps.pysrc/lfx/src/lfx/services/manager.pysrc/lfx/src/lfx/services/model_provider_policy/service.pysrc/lfx/src/lfx/services/policy_bundle/__init__.pysrc/lfx/src/lfx/services/policy_bundle/base.pysrc/lfx/src/lfx/services/policy_bundle/service.pysrc/lfx/src/lfx/services/schema.pysrc/lfx/tests/unit/services/model_provider_policy/test_policy.py
|
Addressed the CodeRabbit review in
For the inline SQLite concern, the production helper already strips Validation: 105 focused backend tests and 36 isolated LFX tests pass; Ruff, formatting, diff checks, and the pre-commit migration validator are also clean. |
Summary
Deployment notes
Validation
git diff --check, Alembic expand-contract validation, and secret detection: passed